package com.polarrose.wsf.client.impl;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.SocketTimeoutException;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xlightweb.IHttpResponse;
import org.xlightweb.PostRequest;
import org.xlightweb.client.HttpClient;

import com.polarrose.wsf.client.HttpProtocolException;
import com.polarrose.wsf.client.Invocation;
import com.polarrose.wsf.client.RequestInvoker;
import com.polarrose.wsf.client.ResultTransformationException;
import com.polarrose.wsf.client.WsfClientException;

/**
 * do not use it - not supported and buggy
 * @author ant
 *
 */
public class XLightwebRequestInvoker implements RequestInvoker {

	public static final String DEFAULT_ENCODING = "UTF-8";

	private Logger logger = LoggerFactory.getLogger(XLightwebRequestInvoker.class);

	public static final String PARAM_VERSION = "Version";
	public static final String PARAM_ACTION = "Action";

	private HttpClient httpClient;
	private boolean appendActionAndVersionAsQueryString;

	public XLightwebRequestInvoker() {
		httpClient = new HttpClient();
		httpClient.setBodyDataReceiveTimeoutMillis(10000);
		httpClient.setFollowsRedirect(true);
		httpClient.setMaxActive(250);
		httpClient.setMaxIdle(50);
		httpClient.setMaxRedirects(3);
		httpClient.setPooled(true);
		httpClient.setResponseTimeoutMillis(10000);
	}

	public void invokeRequest(Invocation<?> invocation) throws WsfClientException {
		String endpoint = invocation.getEndpoint();
		String action = invocation.getAction();
		String version = invocation.getVersion();

		Map<String, String> parameters = invocation.getParameters();

		try {
			
			parameters.put(PARAM_VERSION, version);
			parameters.put(PARAM_ACTION, action);
			
			StringBuilder logEntry = new StringBuilder();
			StringBuilder body = createBody(parameters, logEntry);

			String queryString = "";
			if (appendActionAndVersionAsQueryString) {
				queryString = String.format("?action=%s&version=%s", action, version);
			}

			PostRequest method = new PostRequest(endpoint + queryString, "application/x-www-form-urlencoded; charset=" + DEFAULT_ENCODING, body.toString());

			logger.debug("Requesting URL {}?{}", endpoint, logEntry);

			IHttpResponse httpResponse = httpClient.call(method);

			if (httpResponse.getStatus() == 200) {
				String response = httpResponse.getBlockingBody().readString();
				invocation.getResultTransformer().transform(response);
			} else {
				throw new HttpProtocolException(httpResponse.getStatus(), httpResponse.getBlockingBody().readString());
			}
		} catch (SocketTimeoutException e) {
			throw new WsfClientException("Socket timeout occured.", e);
		} catch (IOException e) {
			logger.error("I/O error", e);
			throw new WsfClientException("An I/O error occured.", e);
		} catch (ResultTransformationException e) {
			throw new WsfClientException("Could not transform JSON response.", e);
		}
	}

	private StringBuilder createBody(Map<String, String> parameters, StringBuilder logEntry) throws UnsupportedEncodingException {
		StringBuilder buff = new StringBuilder();
		for (Entry<String, String> it : parameters.entrySet()) {
			String key = it.getKey();
			String value = it.getValue();

			buff.append(URLEncoder.encode(key, DEFAULT_ENCODING));
			buff.append("=");
			buff.append(URLEncoder.encode(value, DEFAULT_ENCODING));
			buff.append("&");

			logEntry.append(key);
			logEntry.append("=");
			if (value.length() < 1024) {
				logEntry.append(value);
			} else {
				logEntry.append(value.substring(0, 1024) + "...");
			}
			logEntry.append("&");
		}

		if (buff.length() > 0) {
			buff.setLength(buff.length() - 1);
			logEntry.setLength(logEntry.length() - 1);
		}
		return buff;
	}

	private Object formatParameters(PostRequest method) {
		StringBuilder buff = new StringBuilder();
		Set<String> parameterNames = method.getParameterNameSet();
		for (String paramName : parameterNames) {
			buff.append(paramName).append("=").append(Arrays.toString(method.getParameterValues(paramName))).append("&");
		}
		return buff.substring(0, buff.length() - 1);
	}

	public void setAppendActionAndVersionAsQueryString(boolean appendActionAndVersionAsQueryString) {
		this.appendActionAndVersionAsQueryString = appendActionAndVersionAsQueryString;
	}

}
